﻿using BaiTu.CachingEntity;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BaiTu.CachingServices.CacheProvider.LocalCache.LFU
{
    public class LFUCacheContainer : BaseCacheContainer
    {
        public LFUCacheContainer(int capacity)
            : base()
        {
            dic = new ConcurrentDictionary<object, BaseCacheItem>(2, capacity);
            this.capacity = capacity;
        }

        public override object Get(object key)
        {
            BaseCacheItem baseItem;
            if (!this.dic.TryGetValue(key, out baseItem))
                return null;

            if (baseItem == null)
                return null;

            LFUCacheItem item = (LFUCacheItem)baseItem;
            item.HitCount++;
            return item.Value;
        }

        public override void Set(object key, object value, CacheExpireType expireType, DateTime absolutelyExpireTime, TimeSpan slideIncrement)
        {
            BaseCacheItem baseItem;
            if (this.dic.TryGetValue(key, out baseItem))
            {
                LFUCacheItem item = (LFUCacheItem)baseItem;
                item.Key = key;
                item.Value = value;
                item.HitCount++;
                item.ExpireTime = absolutelyExpireTime;
                item.ExpireType = expireType;
                item.SlideIncrement = slideIncrement;
                return;
            }

            if (this.dic.Count >= this.capacity)
            {
                //remove one
                BaseCacheItem lessHitCountItem = GetLessHitCountItem();
                if (lessHitCountItem != null)
                    this.dic.TryRemove(lessHitCountItem.Key, out lessHitCountItem);
            }

            {
                LFUCacheItem item = new LFUCacheItem();
                item.Key = key;
                item.Value = value;
                item.ExpireTime = absolutelyExpireTime;
                item.ExpireType = expireType;
                item.SlideIncrement = slideIncrement;
                this.dic.TryAdd(key, item);
            }
        }

        private LFUCacheItem GetLessHitCountItem()
        {
            LFUCacheItem lessOne = null;
            foreach (var key in this.dic.Keys)
            {
                BaseCacheItem baseItem;
                if (!this.dic.TryGetValue(key, out baseItem))
                    continue;

                LFUCacheItem current = (LFUCacheItem)baseItem;
                if (lessOne == null)
                {
                    lessOne = current;
                    continue;
                }
                if (lessOne.HitCount > current.HitCount)
                    lessOne = current;
            }
            return lessOne;
        }

        public override void Remove(object key)
        {
            if (this.dic.ContainsKey(key))
            {
                BaseCacheItem item = new LFUCacheItem();
                this.dic.TryRemove(key, out item);
            }
        }
    }
}
